package com.zzy.rpc;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;


/**
 * 
 * @author eason
 *
 */
public class RPC {
	
	private static final Log LOG = LogFactory.getLog(RPC.class);
	
	public static <T> T getProxy(Class interfaceClass, String host, int port) {
		LOG.info("Get remote service " + interfaceClass.getName() + " from server " + host + ":" + port);
		return (T) Proxy.newProxyInstance(interfaceClass.getClassLoader(),
				new Class[] { interfaceClass }, new Invoker(interfaceClass, host, port));
	}
	
	private static class Invoker implements InvocationHandler {

		private Class interfaceClass;
		private String host;
		private int port;

		public Invoker(Class interfaceClass, String host, int port) {
			this.interfaceClass = interfaceClass;
			this.host = host;
			this.port = port;
		}

		@Override
		public Object invoke(Object proxy, Method method, Object[] args)
				throws Throwable {

			Invocation invocation = new Invocation(interfaceClass, method.getParameterTypes(), method.getName(), args);
			Client client = new Client(host, port);
			client.invoke(invocation);
			return invocation.getResult();
		}

	}
	
	public static class RPCServer implements Server{

		private int port;
		private boolean isRunning = true;
		private Listener listener;
		private Map<String ,Object> servers = new HashMap<String ,Object>();
		
		public RPCServer(int port) {
			this.port = port;
		}

		@Override
		public void call(Invocation invocation) {
			Object obj = servers.get(invocation.getInterfaces().getName());
			Object result = null;
			if(obj != null) {
				Method method;
				try {
					method = obj.getClass().getMethod(invocation.getMethodName(), invocation.getParameterTypes());
					result = method.invoke(obj, invocation.getParams());
				} catch (SecurityException e) {
					e.printStackTrace();
				} catch (NoSuchMethodException e) {
					e.printStackTrace();
				} catch (IllegalArgumentException e) {
					e.printStackTrace();
				} catch (IllegalAccessException e) {
					e.printStackTrace();
				} catch (InvocationTargetException e) {
					e.printStackTrace();
				}
				invocation.setResult(result);
			}

		}

		@Override
		public int getPort() {
			return port;
		}

		@Override
		public boolean isRunning() {
			return isRunning == true;
		}

		@Override
		public void register(Class interfaceDefiner, Class impl) {
			try {
				servers.put(interfaceDefiner.getName(), impl.newInstance());
			} catch (InstantiationException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			}
		}

		@Override
		public void start() {
			listener = new Listener(this);
			Thread listenerThread = new Thread(listener);
			listenerThread.setName("RPC Server listener on " + port);
//			listenerThread.setDaemon(true);
			listenerThread.start();
		}

		@Override
		public void stop() {
			isRunning = false;
		}
	}
	
}
		
